package com.yjh.study.data.structure.list;

import com.yjh.study.data.structure.LinkedElement;

import java.util.Iterator;
import java.util.Objects;

public class LinkedList<T> extends AbstractList<T> {

    protected final LinkedElement<T> header;
    protected final LinkedElement<T> tail;

    public LinkedList() {
        this.header = new LinkedElement<>();
        this.tail = new LinkedElement<>();

        init();
    }

    private void init() {
        header.setNext(tail);
        tail.setPre(header);

        size = 0;
    }

    @Override
    public void add(T t) {
        addLast(t);
    }

    @Override
    public void remove(T t) {
        LinkedElement<T> current = header;
        while (current.getNext() != null && current.getNext() != tail) {
            current = current.getNext();
            if (Objects.equals(t, current.getData())) {
                remove(current);
                break;
            }
        }
    }

    @Override
    public void clear() {

        if (0 >= size) {
            return;
        }

        LinkedElement<T> current = header.getNext();
        do {
            current.getPre().setNext(null);
            ;
            current.setData(null);
            current.setPre(null);
            current = current.getNext();
        } while (current.getNext() != null);

        init();
    }

    @Override
    public Iterator<T> iterator() {
        return new Itr(header);
    }

    @Override
    public void addFirst(T t) {
        insertBetween(t, header, header.getNext());
    }

    @Override
    public T getFirst() {
        return header.getNext().getData();
    }

    @Override
    public void addLast(T t) {
        insertBetween(t, tail.getPre(), tail);
    }

    protected void insertBetween(T t, LinkedElement<T> pre, LinkedElement<T> post) {
        LinkedElement<T> newElement = new LinkedElement<>(t);
        pre.setNext(newElement);
        newElement.setPre(pre);

        post.setPre(newElement);
        newElement.setNext(post);

        sizeIncrease();
    }

    protected void remove(LinkedElement<T> current) {
        current.getPre().setNext(current.getNext());
        current.getNext().setPre(current.getPre());
        sizeIncrease(-1);
    }

    @Override
    public T getLast() {
        return tail.getPre().getData();
    }

    private class Itr implements Iterator<T> {

        private LinkedElement<T> current;

        public Itr(LinkedElement<T> current) {
            this.current = current;
        }

        @Override
        public boolean hasNext() {
            return current.getNext() != null && current.getNext() != tail;
        }

        @Override
        public T next() {
            current = current.getNext();
            return current.getData();
        }

        @Override
        public void remove() {
            LinkedList.this.remove(current);
        }
    }
}
